import { updateQueue } from './component';

/**
 * 事件绑定
 * 
 * @param {HTMLElement} dom 事件绑定的 DOM 元素
 * @param {Event} eventType 事件类型
 * @param {Function} handler 事件执行函数
 */
export function addEvent(dom, eventType, handler) {

    const store = dom.store ? dom.store : (dom.store = {});

    store[eventType] = handler; // 为每个 DOM 绑定事件，处理函数是委托给 document 了

    // react 从 17 版本开始把事件委托给跟容器 div#root，之前版本是委托在 document 上的
    if (!document[eventType]) {
        document[eventType] = dispathcEvent;
    }
}

/**
 * 合成事件的统一代理函数
 * 
 * @param {*} event 
 */
function dispathcEvent(event) {
    let { type, target } = event;

    const eventType = `on${type}`;
    const syntheticEvent = createSyntheticEvent(event); // 合成事件

    // 异步就把事件设置为批量更新 true
    updateQueue.isBatchingUpdate = true;

    while (target) {
        const { store } = target;
        const handler = store && store[eventType];
        // 执行对应的事件函数(用户自己绑定的事件函数)
        handler && handler(syntheticEvent);
        // 用户执行自己的事件函数事可能阻止冒泡
        if (syntheticEvent.isPropagationStopped) {
            break;
        }
        target = target.parentNode;
    }

    updateQueue.isBatchingUpdate = false;
    updateQueue.batchUpdate(); // 批量更新
}

/**
 * 合成事件
 * 
 * @param {Event} nativeEvent 原生的事件对象
 * @returns 返回合成事件
 */
function createSyntheticEvent(nativeEvent) {
    const syntheticEvent = {};
    for (const key in nativeEvent) {
        // 把 nativeEvent(原生事件)上的属性都拷贝给 syntheticEvent（合成事件对象里）
        syntheticEvent[key] = nativeEvent[key];
    }

    syntheticEvent.nativeEvent = nativeEvent; // 挂载原生事件
    // 在合成事件对象添加一些自己的属性、可以做一些兼容的操作等
    syntheticEvent.isDefaultPrevented = false;
    syntheticEvent.isPropagationStopped = false;
    syntheticEvent.preventDefault = preventDefault;
    syntheticEvent.stopPropagation = stopPropagation;

    return syntheticEvent;
}

function preventDefault() {
    const event = this.nativeEvent;
    if (event.preventDefault) {
        event.preventDefault();
    } else {//IE
        event.returnValue = false;
    }
    this.isDefaultPrevented = true;
}

function stopPropagation() {
    const event = this.nativeEvent;
    if (event.stopPropagation) {
        event.stopPropagation();
    } else {//IE
        event.cancelBubble = true;
    }
    this.isPropagationStopped = true;
}
